#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <libgen.h>
#include <getopt.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "socket.h"
#include "logger.h"

#define LEN 		128
#define BACKLOG 	13 
#define ARRAY_SIZE(x)	(sizeof(x)/sizeof(x[0]))//求一个数组中有多少个元素

int	g_stop = 0;

static 	inline void print_usage(char *progname);
void 	sig_handler(int signum);
int 	socket_server_init(char *listen_ip, int listen_port);
int	database_init(sqlite3 **db);
int	save_data_to_database(sqlite3 *db, char *buf);

int main(int argc, char **argv)
{
	int				sockfd = -1;
	int				connfd = -1;
	int	 			maxfd;
	int				serv_port = 0;
	int				opt;
	int				daemon_run = 0;
	int				rv = -1;
	int				i = 0;
	int				j = 0;
	int				fds_array[1024];
	char				buf[128];
	char				*sql;
	char				*zErrMsg = 0;
	char				*progname;
	char				*listen_ip = NULL;
	fd_set				rdset;
	struct sockaddr_in		cliaddr;
	socklen_t			len = sizeof(cliaddr);
	sqlite3				*db = NULL;

	struct option	long_options[] = 
	{
		{"daemon", no_argument, NULL, 'd'},
		{"port", required_argument, NULL, 'p'},
		{"ipaddr", required_argument, NULL, 'i'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
	};

	progname = basename(argv[0]);

	while( -1 != (opt = getopt_long(argc, argv, "dp:i:h", long_options, NULL)) )
	{
		switch(opt)
		{
			case 'd':
				daemon_run = 1;
				break;
			case 'p':
				serv_port = atoi(optarg);
				break;
			case 'i':
				listen_ip = optarg;
				break;
			case 'h':
				print_usage(progname);
				return EXIT_SUCCESS;

			default:
				break;
		}
	}

	if(!serv_port)
	{
		print_usage(progname);
		return -1;
	}

	//后台运行
	if( daemon_run )
	{
		daemon(1, 1);
	}

	//打开日志系统
	if(logger_init("log.txt", LOG_LEVEL_DEBUG) < 0)
	{
		fprintf(stderr, "initial logger system failure\n");
		return 2;
	}

	//打开数据库
	rv = database_init(&db);
	if(rv < 0)
	{
		log_error("Open database failure: %s\n", strerror(errno));
		return -1;
	}

	//调socket()、bind()、listen()
	if((sockfd = socket_server_init(NULL, serv_port)) < 0)
	{
		log_error(" %s server listen on port %d failure:%s\n", argv[0], serv_port, strerror(errno));
		return -2;
	}
	log_info("%s server start to listen on port[%d]\n", argv[0], serv_port);

	//遍历整个数组，将其中成员都置为-1
	for(i=0; i<ARRAY_SIZE(fds_array); i++)
	{
		fds_array[i] = -1;
	}
	//将sockfd作为数组的第一个元素(最开始没有客户端的fd，只有socket创建的sockfd)
	fds_array[0] = sockfd;

	while(!g_stop)
	{
		//清空集合
		FD_ZERO(&rdset);
		//遍历数组
		for(i=0; i<ARRAY_SIZE(fds_array); i++)
		{
			if(fds_array[i] < 0)//该文件描述符不存在
			{
				continue;
			}

			//若当前的fd大于maxfd，就将maxfd更新为该fd
			maxfd = fds_array[i]>maxfd ? fds_array[i] : maxfd;
			//将整个数组的元素加入监听集合(此时该集合里只有sockfd)
			FD_SET(fds_array[i], &rdset);
		}

		//调select()开始监听集合
		rv = select(maxfd+1, &rdset, NULL, NULL, NULL);
		if(rv < 0)
		{
			log_warn("Select failure: %s\n", strerror(errno));
			break;
		}
		else if(0 == rv)
		{
			log_debug("Select get timeout\n");
			continue;
		}
		else
		{
			log_info("Select() successfully!\n");
		}

		//判断指定描述符是否在集合中
		//rv > 0, 要判断是谁发生的事件：sockfd（接下来要调accept）或 accept()返回的connfd（接下来应该调读和写）
		//第一次一定是sockfd发生，然后要调accept(), accept()返回一个新的文件描述符：connfd
		if( FD_ISSET( sockfd, &rdset) )
		{
			if( (connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len)) < 0 )
			{
				log_error("Accept new client failure: %s\n", strerror(errno));
				continue;
			}

			//设定一个标志：是否能在数组找到空位； 若能找到，则将该connfd加入select()监听的数组
			int	found = 0;
			for(i=0; i<ARRAY_SIZE(fds_array); i++)
			{
				if(fds_array[i] < 0)
				{
					log_info("Accept new client[%d] and add it into array\n", connfd);
					fds_array[i] = connfd;
					found = 1;
					break;
				}
			}

			//若该数组没有空位， 则关闭该connfd
			if(!found)
			{
				log_warn("Accept new client[%d] but full, so refuse it\n", connfd);
				close(connfd);
			}
		}

		//是已经连上的connfd发生了，就调read()
		else
		{
			for(i=0; i<ARRAY_SIZE(fds_array); i++)
			{
				if(fds_array[i] < 0 || !FD_ISSET(fds_array[i], &rdset))
				{
					continue;
				}

				memset(buf, 0, sizeof(buf));
				rv = read(fds_array[i], buf, sizeof(buf));

				//read()失败，关闭该fd并将其从数组中释放
				if(rv <= 0)
				{
					log_error("Socket[%d] read data from client failure or get disconnect.\n", fds_array[i]);
					log_info("\nStart waiting conncet with client again...\n");

					close(fds_array[i]);
					fds_array[i] = -1;
					break;
				}
				else
				{
					log_info("Socket [%d] read client data: %s\n", fds_array[i], buf);
				}

				
				//将数据传到数据库里
				rv = save_data_to_database(db, buf);
				if(rv < 0)
				{
					log_error("Send data to database failure: %s\n", strerror(errno));
					return -3;
				}
				log_info("Send data to database OK!\n");
			}
		}	
	}
	sqlite3_close(db);
	logger_term();
}


//安装信号
void sig_handler(int signum)
{
	if(SIGTERM == signum)
	{
		log_error("SIGTERM signal detected!\n");
		g_stop = 1;
	}
}

static inline void print_usage(char *progname)
{
	printf("Usage: %s [OPTION]...\n", progname);

	printf(" %s is a socket server program, which used to verify client and echo back string from it\n", progname);
	printf("\nMandatory arguments to long options are mandatory for short options too:\n");

	printf(" -d[daemon] set program running on background\n");
	printf(" -p[port  ] Socket server port address\n");
	printf(" -i[ipaddr] Socket server IP address\n");
	printf(" -h[help  ] Display this help information\n");

	printf("\nExample: %s -d -p 8900\n", progname);
	return;
}


